use std::{cell::RefCell, ops::Deref, rc::Rc};

use winit::event_loop::EventLoop;

use crate::{

    function::{render::{render_system::AliceRender, rhi::{RhiInitInfo, Rhi}}, window::AliceWindow, ui::ui_context::AliceUI, resource::resource_system::{ AliceResource}, swap::swap_layer::AliceSwap, command::canvas2d::Canvas2dCommand, script::script_engine::AliceScriptEngine, scene::scene::SceneServer}, math::{Matrix4f, Vector2u, Vector2f}, config::AliceConfig, input::{InputImpl, INPUT},
};

#[derive(Clone)]
pub struct AliceContext(Rc<RefCell<ContextImpl>>);

impl Deref for AliceContext {
    type Target = Rc<RefCell<ContextImpl>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl AliceContext {
    pub fn new(event_loop: &EventLoop<()>) -> Self {
        Self(Rc::new(RefCell::new(ContextImpl::new(event_loop))))
    }

    pub fn begin_frame(&mut self) {
        if let Some(input) = unsafe {
            INPUT.get_mut() 
        }{
            input.begin_frame()
        }
        self.0.borrow_mut().render.begin_frame();
    }
    
    /// 更新每帧需要的公共数据
    pub fn update_scene_data(&mut self , data:Matrix4f) {
        self.0.borrow_mut().render.update_scene_camera(data);
    }
}


/// 获取内部共享模块
impl AliceContext {

    
    pub fn rhi(&self) -> Rhi {
        self.borrow().render.rhi.clone()
    }

    pub fn config(&self) -> AliceConfig {
        self.borrow().config.clone()
    }

    pub fn set_render_pipeline_output(&mut self, view:wgpu::TextureView) {
        self.0.borrow_mut().render.set_custom_output_render(view);
    }

    pub fn script(&self) -> AliceScriptEngine {
        self.0.borrow().script.clone()
    }

}

/// 修改引擎默认配置
impl AliceContext {
    pub fn resize(&mut self,size:Vector2u) {
        self.0.borrow_mut().set_window_size(size)
    }

    pub fn set_scale_factory(&mut self,scale:f32) {
        self.0.borrow_mut().set_scale_factory(scale)
    }

    pub fn set_project_title(&mut self, title:&str) {
        self.0.borrow_mut().set_project_name(title);
    }

    pub fn update_screen_info(&mut self,pos:Vector2f, size:Vector2u) {
       
        self.0.borrow_mut().update_screen_info(pos, size)
    }
}

/// draw canvas 2d operator

impl AliceContext {
    pub fn draw_canvas_command(&mut self , cmd:Canvas2dCommand) {
        self.0.borrow_mut().swap.canvas_command(cmd);
    }
}

pub struct ContextImpl {
    pub(crate) window:  AliceWindow,
    pub        render:  AliceRender,
    pub(crate) resource:AliceResource,
    pub(crate) ui:      AliceUI,
    pub(crate) swap:    AliceSwap,

    pub(crate) config:  AliceConfig,
    pub(crate) script:  AliceScriptEngine,

}

impl ContextImpl{
    pub fn new(event_loop: &EventLoop<()>) -> Self {
        
        let config      = AliceConfig::new();
        let window      = AliceWindow::new(event_loop,&config);

        let rhi_init_info = RhiInitInfo { window:&window };
        let rhi = Rhi::new(rhi_init_info);
        
        let resource  = AliceResource::new(rhi.clone(),&config);
        let render      = AliceRender::new(rhi.clone(),resource.clone(),&config);
        let ui              = AliceUI::new(&config);
        let swap          = AliceSwap::new();

        let input = InputImpl::new(&config);

        let server = SceneServer::new();
        SceneServer::initialize_scene_server(server);

        unsafe { INPUT.set(input); }
        let script = AliceScriptEngine::new();
        Self { window, render , ui , resource , swap,config ,script }
    }

    pub fn set_window_size(&mut self, size:Vector2u) {
        self.config.set_window_size(size);
        self.render.resize();
        if let Some(input) = unsafe {
            INPUT.get_mut()
        } {
            input.resize(&self.config)
        }
    
    }

    pub fn update_screen_info(&mut self,pos:Vector2f, size:Vector2u) {
        self.ui.update_screen_info(pos, size);
        self.config.set_screen_size(size);
        self.render.resize();
        if let Some(input) =unsafe {
            INPUT.get_mut()
        }  {
            input.resize(&self.config)
        }
        
    }

    pub fn set_scale_factory(&mut self,scale:f32) {
        self.config.set_scale_factory(scale);
    }

    pub fn set_project_name(&mut self, name:& str) {
        self.config.set_project_name(name);

        self.window.set_project_title(name)
    }

  
    pub fn tick(&mut self) {

        self.swap.submit_canvas_to_render(&mut self.render);
        self.render.tick();
    }

}

